/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.diplomka.servicelayer.gamelogic.managerimpl;

import cz.fi.muni.diplomka.common.enumeration.BuildingType;
import cz.fi.muni.diplomka.common.enumeration.MovementType;
import cz.fi.muni.diplomka.common.enumeration.ResourceType;
import cz.muni.fi.diplomka.datalayer.dao.IncomingResourcesDao;
import cz.muni.fi.diplomka.datalayer.dao.ShopOfferDao;
import cz.muni.fi.diplomka.datalayer.daoimpl.IncomingResourcesDaoImpl;
import cz.muni.fi.diplomka.datalayer.daoimpl.ShopOfferDaoImpl;
import cz.muni.fi.diplomka.datalayer.entity.IncomingResourcesEntity;
import cz.muni.fi.diplomka.datalayer.entity.ShopOfferEntity;
import cz.muni.fi.diplomka.servicelayer.dto.BuildingInCity;
import cz.muni.fi.diplomka.servicelayer.dto.IncomingResources;
import cz.muni.fi.diplomka.servicelayer.dto.ShopOffer;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.CityBuildingManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.CityManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.CityResourcesManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.MarketManager;
import cz.muni.fi.diplomka.servicelayer.gamelogic.resolver.TravelDistanceResolver;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;

/**
 *
 * @author Jakub Kutil
 */
public class MarketManagerImpl implements MarketManager {

    private ShopOfferDao shopOfferDao = new ShopOfferDaoImpl();
    private IncomingResourcesDao incomingResourcesDao = new IncomingResourcesDaoImpl();
    private CityResourcesManager cityResourcesManager = new CityResourcesManagerImpl();
    private CityBuildingManager cityBuildingManager = new CityBuildingManagerImpl();
    private TravelDistanceResolver travelDistanceResolver = new TravelDistanceResolver();
    private CityManager cityManager = new CityManagerImpl();
    private Mapper mapper = new DozerBeanMapper();

    @Override
    public void createShopOffer(ShopOffer shopOffer) {
        if (shopOffer == null) {
            throw new IllegalArgumentException("ShopOffer can not be null.");
        }
        cityResourcesManager.lowerResource(shopOffer.getCityId(), shopOffer.getSoldResource(), shopOffer.getSoldResourceCount());
        shopOfferDao.addShopOffer(mapper.map(shopOffer, ShopOfferEntity.class));
    }

    @Override
    public void editShopOffer(ShopOffer shopOffer) {
        if (shopOffer == null) {
            throw new IllegalArgumentException("ShopOffer can not be null.");
        }
        shopOfferDao.editShopOffer(mapper.map(shopOffer, ShopOfferEntity.class));
    }

    @Override
    public void deleteShopOffer(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("Id can not be null.");
        }

        shopOfferDao.deleteShopOffer(id);
    }

    @Override
    public void cancelShopOffer(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("Id can not be null.");
        }
        ShopOffer shopOffer = this.getShopOfferById(id);
        cityResourcesManager.increaseResource(shopOffer.getCityId(), shopOffer.getSoldResource(), shopOffer.getSoldResourceCount());
        shopOfferDao.deleteShopOffer(id);
    }

    @Override
    public ShopOffer getShopOfferById(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("Id can not be null.");
        }

        return mapper.map(shopOfferDao.getShopOfferById(id), ShopOffer.class);
    }

    @Override
    public List<ShopOffer> getShopOffersOfPlayer(Long playerId) {
        if (playerId == null) {
            throw new IllegalArgumentException("PlayerId can not be null.");
        }
        List<ShopOffer> shopOfferList = new ArrayList<>();
        for (ShopOfferEntity shopOfferEntity : shopOfferDao.getAllShopOffersOfPlayer(playerId)) {
            shopOfferList.add(mapper.map(shopOfferEntity, ShopOffer.class));
        }

        return shopOfferList;
    }

    @Override
    public List<ShopOffer> getShopOffersNotOfPlayer(Long playerId) {
        if (playerId == null) {
            throw new IllegalArgumentException("PlayerId can not be null.");
        }
        List<ShopOffer> shopOfferList = new ArrayList<>();
        for (ShopOfferEntity shopOfferEntity : shopOfferDao.getAllShopOffersNotOfPlayer(playerId)) {
            shopOfferList.add(mapper.map(shopOfferEntity, ShopOffer.class));
        }

        return shopOfferList;
    }

    @Override
    public void buyShopOffer(Long shopOfferId, Long buyerId) {
        if (shopOfferId == null) {
            throw new IllegalArgumentException("ShopOfferId can not be null.");
        }
        if (buyerId == null) {
            throw new IllegalArgumentException("BuyerId can not be null.");
        }
        ShopOffer shopOffer = this.getShopOfferById(shopOfferId);

        if (cityResourcesManager.getResourceCount(buyerId, shopOffer.getWantedResource()) >= shopOffer.getWantedResourceCount()) {
            cityResourcesManager.lowerResource(buyerId, shopOffer.getWantedResource(), shopOffer.getWantedResourceCount());
        }

        this.deleteShopOffer(shopOfferId);

        double distance = travelDistanceResolver.calculateDistance(cityManager.getCityById(buyerId).getCoordinates().getX_coordinate(),
                cityManager.getCityById(buyerId).getCoordinates().getX_coordinate(), cityManager.getCityById(shopOffer.getCityId()).getCoordinates().getX_coordinate(), cityManager.getCityById(shopOffer.getCityId()).getCoordinates().getX_coordinate());

        List<BuildingInCity> markets = cityBuildingManager.getAllBuildingsInCityOfType(buyerId, BuildingType.Market);
        markets.addAll(cityBuildingManager.getAllBuildingsInCityOfType(shopOffer.getCityId(), BuildingType.Market));

        double multiplier = 1;
        for (BuildingInCity market : markets) {
            multiplier = multiplier - (market.getLevel() / 100);
        }

        distance = distance * multiplier;

        IncomingResourcesEntity buyerIncomingResourcesEntity = new IncomingResourcesEntity();
        buyerIncomingResourcesEntity.setCityTo(buyerId);
        buyerIncomingResourcesEntity.setCityFrom(shopOffer.getCityId());
        buyerIncomingResourcesEntity.setResource(shopOffer.getSoldResource());
        buyerIncomingResourcesEntity.setResourceCount(shopOffer.getSoldResourceCount());
        buyerIncomingResourcesEntity.setTimeOfArrival(new Timestamp((long) (System.currentTimeMillis() + distance * 1000 * 60 * 20)));
        buyerIncomingResourcesEntity.setMovementType(MovementType.Shop);

        IncomingResourcesEntity sellerIncomingResourcesEntity = new IncomingResourcesEntity();
        buyerIncomingResourcesEntity.setCityTo(shopOffer.getCityId());
        buyerIncomingResourcesEntity.setCityFrom(buyerId);
        sellerIncomingResourcesEntity.setResource(shopOffer.getWantedResource());
        sellerIncomingResourcesEntity.setResourceCount(shopOffer.getWantedResourceCount());
        sellerIncomingResourcesEntity.setTimeOfArrival(new Timestamp((long) (System.currentTimeMillis() + distance * 1000 * 60 * 20)));
        sellerIncomingResourcesEntity.setMovementType(MovementType.Shop);

        incomingResourcesDao.addIncomingResources(buyerIncomingResourcesEntity);
        incomingResourcesDao.addIncomingResources(sellerIncomingResourcesEntity);
    }

    @Override
    public void sendResources(Long cityFrom, Long cityTo, ResourceType resourceType, int count, MovementType movementType) {
        if (cityFrom == null) {
            throw new IllegalArgumentException("CityFrom can not be null.");
        }
        if (cityTo == null) {
            throw new IllegalArgumentException("CityTo can not be null.");
        }
        if (resourceType == null) {
            throw new IllegalArgumentException("ResourceType can not be null.");
        }
        if (movementType == null) {
            throw new IllegalArgumentException("MovementType can not be null.");
        }

        IncomingResourcesEntity incomingResourcesEntity = new IncomingResourcesEntity();
        incomingResourcesEntity.setCityFrom(cityFrom);
        incomingResourcesEntity.setCityTo(cityTo);
        incomingResourcesEntity.setResource(resourceType);
        incomingResourcesEntity.setResourceCount(count);
        incomingResourcesEntity.setMovementType(movementType);

        double distance = travelDistanceResolver.calculateDistance(cityManager.getCityById(cityFrom).getCoordinates().getX_coordinate(),
                cityManager.getCityById(cityFrom).getCoordinates().getY_coordinate(), cityManager.getCityById(cityTo).getCoordinates().getX_coordinate(), cityManager.getCityById(cityTo).getCoordinates().getY_coordinate());

        incomingResourcesEntity.setTimeOfArrival(new Timestamp((long) (System.currentTimeMillis() + distance * 1000 * 60 * 20)));

        incomingResourcesDao.addIncomingResources(incomingResourcesEntity);

        cityResourcesManager.lowerResource(cityFrom, resourceType, count);
    }

    @Override
    public List<IncomingResources> getIncomingResourcesOfCity(Long cityId) {
        if (cityId == null) {
            throw new IllegalArgumentException("CityId can not be null.");
        }
        List<IncomingResources> incomingResourceList = new ArrayList<>();
        for (IncomingResourcesEntity incomingResourcesEntity : incomingResourcesDao.getAllIncomingResourcesOfCity(cityId)) {
            incomingResourceList.add(mapper.map(incomingResourcesEntity, IncomingResources.class));
        }

        return incomingResourceList;
    }

    @Override
    public List<IncomingResources> getOutcomingResourcesOfCity(Long cityId) {
        if (cityId == null) {
            throw new IllegalArgumentException("CityId can not be null.");
        }
        List<IncomingResources> outcomingResourceList = new ArrayList<>();
        for (IncomingResourcesEntity outcomingResourcesEntity : incomingResourcesDao.getAllOutcomingResourcesOfCity(cityId)) {
            outcomingResourceList.add(mapper.map(outcomingResourcesEntity, IncomingResources.class));
        }

        return outcomingResourceList;
    }

    @Override
    public List<IncomingResources> getAllIncomingResources() {

        List<IncomingResources> incomingResourceList = new ArrayList<>();
        for (IncomingResourcesEntity incomingResourcesEntity : incomingResourcesDao.getAllIncomingResources()) {
            incomingResourceList.add(mapper.map(incomingResourcesEntity, IncomingResources.class));
        }

        return incomingResourceList;
    }

    @Override
    public void deleteIncomingResources(Long id) {
        if (id == null) {
            throw new IllegalArgumentException("Id can not be null.");
        }

        incomingResourcesDao.deleteIncomingResources(id);
    }
}
